为了账号安全,请及时绑定邮箱和手机立即绑定

Android框架Anko的SQLite模块知识点总结

标签:
Android

你是否尝试过使用Android的游标(Cursors)来解析SQLite的查询结果?你必须编写大量的样板代码,仅仅为了解析查询的结果行,并把它包含在数不清的try..finally中,适当的关闭所有打开的资源。

Anko提供了大量的扩展函数,来简化SQLite数据库的操作。

内容

在你的工程中使用Anko SQLite

添加 anko-sqlite 依赖至你的 build.gradle文件中:

    dependencies {        compile "org.jetbrains.anko:anko-sqlite:$anko\_version"
    }

访问数据库

如果你使用 SQLiteOpenHelper,你一般会调用 getReadableDatabase() 或是 getWritableDatabase() (在生产代码中结果其实是一样的),但是你之后必须调用接收到的SQLiteDatabase对象的 close() 方法。你也需要在某些地方对帮助类进行缓存,并且如果你是在几个不同的线程中使用它,你必须进行并发处理。以上所用的东西都挺困难的。那也是为什么Android开发者不热衷与使用默认的SQLite API而倾向于使用像ORM(对象关系映射)这样的重量级包装。

Anko提供了一个特别的类 ManagedSQLiteOpenHelper 用来替换默认的哪一个。 它是这样子使用的:

    class MyDatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1) {        companion object {            private var instance: MyDatabaseOpenHelper? = null

            @Synchronized
            fun getInstance(ctx: Context): MyDatabaseOpenHelper {                if (instance == null) {
                    instance = MyDatabaseOpenHelper(ctx.getApplicationContext())
                }                return instance!!
            }
        }        override fun onCreate(db: SQLiteDatabase) {            // Here you create tables
            db.createTable("Customer", ifNotExists = true, 
                        "id" to INTEGER + PRIMARY\_KEY + UNIQUE,                        "name" to TEXT,                        "photo" to BLOB)
        }        override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {            // Here you can upgrade tables, as usual
            db.dropTable("User", true)
        }
    }    // Access property for Context
    val Context.database: MyDatabaseOpenHelper        get() = MyDatabaseOpenHelper.getInstance(getApplicationContext())

感觉怎么样? 不用在把你的代码放大 try 语句块中,现在你只需这么写:

    database.use {        // `this` is a SQLiteDatabase instance
    }

数据库在执行 {} 中的所有代码后会被关闭。

异步调用的例子:

    class SomeActivity : Activity() {        private fun loadAsync() {
            async(UI) {                val result = bg { 
                    database.use { ... }
                }
                loadComplete(result)
            }
        }
    }

之前提到的所有方法都可能抛出 SQLiteException。你必须自己去处理它,Anko没有理由假装那些错误不会发生。

创建和删除表

使用Anko你可以很方便的创建一个表,或是删除一个已经存在的表。语法很直接:

    database.use {
        createTable("Customer", true, 
            "id" to INTEGER + PRIMARY_KEY + UNIQUE,            "name" to TEXT,            "photo" to BLOB)
    }

在SQLite中,有5个主要类型: NULLINTEGERREALTEXT and BLOB。但是每一行中可能有像 PRIMARY KEY 或是 UNIQUE这样的修饰符。 你可以使用加号添加到类型名的后面。

使用 dropTable 函数,删除一个表:

    dropTable("User", true)

插入数据

同常情况下, 你需要一个 ContentValues 实例来往表中插入一行数据。这里有一个例子:

    val values = ContentValues()
    values.put("id", 5)
    values.put("name", "John Smith")
    values.put("email", "user@domain.org")
    db.insert("User", null, values)

Anko让你直接将想插入的值作为 insert() 函数的参数:

    // Where db is an SQLiteDatabase
    // eg: val db = database.writeableDatabase
    db.insert("User", 
        "id" to 42,        "name" to "John",        "email" to "user@domain.org"
    )

或者在 database.use 中使用:

    database.use {
        insert("User", 
            "id" to 42,            "name" to "John",            "email" to "user@domain.org"
    }

注意:在上面的例子中 database 是一个 database helper 的实例,而 db 是一个 SQLiteDatabase 对象。

insertOrThrow()replace()replaceOrThrow() 等函数也存在并且有着相同的语义。

查询数据

Anko提供了一个很方便的查询建造器。它可以通过 db.select(tableName, vararg columns) 创建,其中 db 是 SQLiteDatabase的一个实例。

方法描述
column(String)Add a column to select query
distinct(Boolean)Distinct query
whereArgs(String)Specify raw String where query
whereArgs(String, args)Specify a where query with arguments
whereSimple(String, args)Specify a where query with ? mark arguments
orderBy(String, [ASC/DESC])Order by this column
groupBy(String)Group by this column
limit(count: Int)Limit query result row count
limit(offset: Int, count: Int)Limit query result row count with an offset
having(String)Specify raw having expression
having(String, args)Specify a having expression with arguments

加粗的函数使用一种特殊的方法来解析参数. 他们允许你以任意的顺序对参数进行赋值:

    db.select("User", "name")
        .whereArgs("(_id > {userId}) and (name = {userName})",            "userName" to "John",            "userId" to 42)

在这里, {userId} 将会被 42 替换, {userName} 被 'John'替换。 如果类型不是数字类型 (IntFloat 等等。) 或 Boolean 型,值可能会被转义。 对于其他对一些类型,可能会使用到 toString()

whereSimple 接受 String 类型到参数。 它工作起来和 SQLiteDatabase 中的 <a target="_blank title=" null"="" style="word-wrap: break-word; color: rgb(59, 67, 72); word-break: break-all;">query())一样 (问号标记 ? 会被参数中的真实值所替代)。

我们怎样执行查询操作?使用 exec() 函数。她接受一个 Cursor.() -> T类型的扩展函数。 它接收扩展函数然后它来关闭 Cursor ,你不要自己去做这件事:

    db.select("User", "email").exec {        // Doing some stuff with emails
    }

解析查询结果

我们拥有一些 Cursor,我们怎么将它解析成一个标准的类呢? Anko提供了 parseSingleparseOpt 和 parseList 函数,使得这件事做起来更加简单。

方法描述
parseSingle(rowParser): TParse exactly one row
parseOpt(rowParser): T?Parse zero or one row
parseList(rowParser): List<T>Parse zero or more rows

注意: 如果接收到的 Cursor包含超过一行的数据, parseSingle() 和 parseOpt() 将会抛出异常。

现在的问题是: 什么是 rowParser?每个函数都支持两种不同类型的解析器: RowParser 和 MapRowParser:

    interface RowParser<T> {        fun parseRow(columns: Array<Any>): T
    }    interface MapRowParser<T> {        fun parseRow(columns: Map<String, Any>): T
    }

如果你想让你的查询效率更高,RowParser (当你必须知道每一列的序号)。 parseRow 接受一个 Any 列表( Any 可以是除了 LongDoubleString 或是 ByteArray 之外的任何类型)。 MapRowParser,你可以使用字段的名称来获取它的值。

Anko 已经提供了以下这些单列单行解析器:

  • ShortParser

  • IntParser

  • LongParser

  • FloatParser

  • DoubleParser

  • StringParser

  • BlobParser

你也可以在类的构造函数中创建一个行解析器。假设你有一个类:

    class Person(val firstName: String, val lastName: String, val age: Int)

解析就像下面这么简单:

    val rowParser = classParser<Person>()

目前为止,对于主构造函数中存在可选参数的类不支持创建解析器。注意,构造函数是通过反射进行调用的,对于很大的数据集,还是使用 RowParser 比较好。

如果你使用了 db.select() 建造器, 你可以直接在里面调用 parseSingleparseOpt 和 parseList 并传入一个适当的解析器。

自定义行解析器

直接上实例,为 (Int, String, String) 这些列创建一个解析器。 最幼稚的做法是这样子:

    class MyRowParser : RowParser<Triple<Int, String, String>> {        override fun parseRow(columns: Array<Any>): Triple<Int, String, String> {            return Triple(columns[0] as Int, columns[1] as String, columns[2] as String)
        }
    }

很好,现在在你的代码中有三个显式的转换。让我们使用rowParser函数来去除他们:

    val parser = rowParser { id: Int, name: String, email: String ->
        Triple(id, name, email)
    }

就是这样子! rowParser 将所有转换都隐藏了起来,你可以按照自己的心情命名lambda表达式中的参数。

Cursor 流

Anko提供了一种函数式的方式访问 SQLite的 Cursor 。只要调用 cursor.asSequence() 或 cursor.asMapSequence() 扩展函数来获取数据行的序列。 不要忘记关闭 Cursor :)

更新值

为其中的一个user赋予一个新的名字:

    update("User", "name" to "Alice")
        .where("_id = {userId}", "userId" to 42)
        .exec()

Update 也有一个 whereSimple() 方法,如果你是一个传统的人就使用它:

    update("User", "name" to "Alice")
        .`whereSimple`("_id = ?", 42)
        .exec()

事务

有一个叫做 transaction() 的函数,允许你将几个数据库操作放到一个SQLite的事务中。

    transaction {        // Your transaction code
    }

当 {} 块中没有抛出任何异常时,事务才会标记为成功。

原文链接:http://www.apkbus.com/blog-822717-72706.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消